package com.ironman.analyze.core.compute.base;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.ironman.analyze.core.context.TrainingContext;
import com.ironman.common.entity.BaseData;
import com.ironman.common.structure.FixedSizeLinkedBlockingDeque;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;


@Slf4j
@Getter
public abstract class BaseAverageComputer<T extends BaseData> {

    private final FixedSizeLinkedBlockingDeque<T> linkedBlockingDeque;

    /**
     * 移动平均窗口大小
     */
    private final Integer windowSize;

    /**
     * 共享变量
     */
    private final TrainingContext trainingContext;

    public BaseAverageComputer(int windowSize, TrainingContext trainingContext) {
        this.windowSize = windowSize;
        this.trainingContext = trainingContext;
        this.linkedBlockingDeque = new FixedSizeLinkedBlockingDeque<>(windowSize);
    }

    public void windowData(T t, BiConsumer<Date, Double> windowDataConsumer) {
        this.linkedBlockingDeque.offerLast(t);
        List<T> dataList = this.linkedBlockingDeque.subList(windowSize, true);
        if (CollectionUtil.isEmpty(dataList)) {
            log.debug("{} 未能达到窗口大小，目标窗口大小为{}", this.getClass().getSimpleName(), windowSize);
            return;
        }

        // 移除不在窗口数据
        Date maxTs = dataList.get(windowSize - 1).getTs();
        dataList = dataList.stream().filter(n -> Objects.nonNull(n.calValue())).filter(n -> DateUtil.between(n.getTs(), maxTs, DateUnit.SECOND) < windowSize).collect(Collectors.toList());
        log.debug("{} 开启窗口计算 目标窗口大小 {} 实际数据大小 {}", this.getClass().getSimpleName(), windowSize, dataList.size());
        if (dataList.isEmpty()) {
            windowDataConsumer.accept(maxTs, null);
        } else {
            double average = dataList.stream().mapToDouble(BaseData::calValue).average().orElse(0);
            windowDataConsumer.accept(maxTs, average);
        }
    }
}
